package com.atguigu.gmall.seckill.component;

import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.model.activity.SeckillGoods;
import com.atguigu.gmall.seckill.service.SecKillService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 监听通知事件
 */
@Component
public class SecKillLocalCache implements MessageListener {
    @Autowired
    SecKillService secKillService;

    public static final String  channel_key = "seckillgoodsup";
    public static final String goods_up="up";
    /**
     * 准备一个保存当天需要秒杀的所有商品的集合
     */
    private Map<Long, SeckillGoods> cache = new ConcurrentHashMap<>();


    /**
     * 从缓存中获取某个商品
     * @param skuId
     * @return
     */
    public SeckillGoods getSeckillGoodsBySkuId(Long skuId){
//        去分布式信号量拿
//        boolean b  = getSemphore(skuId).tryAcquire();
//        if(!b){
//           int i =  getSemphore(skuId).availiablePremites;
//            cache.put(skuId,new XXX(0))//
//        }
        SeckillGoods goods = cache.get(skuId);
        if(goods == null && cache.size()==0){
            //重新加载一遍数据
            cacheDataLoad();
            goods = cache.get(skuId);
        }
        return goods;
    }

    /**
     * redis消息到达；
     * @param message  消息内容
     * @param pattern  通道名 seckillgoodsup
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
        //广播的通道
        String channel = new String(message.getChannel());
        //广播的内容
        String body = new String(message.getBody());

        if(channel.equals(channel_key)){
            if(goods_up.equals(body)){
                //TODO 同步需要秒杀的商品到本地缓存，上架当天新品
                //redis中有定时任务把当天的秒杀商品上架了
                cache.clear(); //清除前一天本地缓存的数据
                System.out.println("监听到当天需要秒杀的所有商品的内容已经缓存");
                cacheDataLoad();
            }
        }else if (channel.equals(RedisConst.SECKILL_GOODS_OUT)){
            //广播的内容就是哪些skuId没有库存了，
            long skuId = Long.parseLong(body);
            SeckillGoods goods = cache.get(skuId);
            //设置没库存
            goods.setStockCount(goods.getNum());
        }
    }

    /**
     * 可能由于机器重启缓存会自己变空
     */
    private void cacheDataLoad() {
        if(cache.size() == 0){
            synchronized (this){
                //双检查
                if(cache.size() == 0){
                    List<SeckillGoods> currentSeckillGoods = secKillService.getCurrentSeckillGoods();
                    //上架到本地二级缓存
                    if(!CollectionUtils.isEmpty(currentSeckillGoods)){
                        for (SeckillGoods good : currentSeckillGoods) {
                            cache.put(good.getSkuId(),good);
                        }
                    }
                }
            }
        }
    }

    /**
     * 返回当天所有需要秒杀的商品
     * @return
     */
    public List<SeckillGoods> todaySeckillGoods(){
        //1、先看二级本地缓存中是否存在
        Collection<SeckillGoods> values = getCacheGoods();
        if(!CollectionUtils.isEmpty(values)){
            //map中按照秒杀时间排好顺序
            List<SeckillGoods> collect = values.stream().sorted((t1, t2) -> {
                return (int) (t1.getStartTime().getTime() - t2.getStartTime().getTime());
            }).collect(Collectors.toList());
            return collect;
        }else {
            //查询redis数据，重新同步，重新加载数据
            cacheDataLoad();
            return getCacheGoods();
        }
    }

    private List<SeckillGoods> getCacheGoods() {
        Collection<SeckillGoods> values = cache.values();
        if (CollectionUtils.isEmpty(values)){
            return null;
        }
        List<SeckillGoods> collect = values.stream().sorted((t1, t2) -> {
            return (int) (t1.getStartTime().getTime() - t2.getStartTime().getTime());
        }).collect(Collectors.toList());
        return collect;
    }
}
